#include "Platforms.h"

#ifdef WIN32
#include <vld.h>
#endif

#ifdef _ANDROID
#include <jni.h>
#endif

#include <iostream>
#include "ESContext.h"
#include "Shader.h"
#include "Vertex.h"
#include "Timer.h"
#include "Camera.h"
#include "Model.h"
#include "Texture.h"
#include "Object.h"
#include "TGA.h"
#include "ResourceManager.h"
#include "SceneManager.h"
#include "FileManager.h"
#include "InputManager.h"
#include "FrameBuffer.h"
#include "Matrix.h"

const int CIR_SIZE = 100;
const float SCRRATIO = (float)SCREEN_W / SCREEN_H;
const float R = 0.1f;
float angle = 0;
ResourceManager* RM;
SceneManager* SM; 
FileManager* FM; 
InputManager* IM; 
FrameBuffer* FBSelection;
FrameBuffer* FBRender;
FrameBuffer* FBShadow;
Object* Screen;
float rad;
Texture textureShadow;
Texture* temp[1];

void Init()
{
	RM = ResourceManager::GetInstance();
	SM = SceneManager::GetInstance();
	FM = FileManager::GetInstance();
	IM = InputManager::GetInstance();
	#ifdef WIN32
		FM->Init((char *)"../Resources/");
	#endif
	#ifdef _ANDROID
		FM->Init((char *)"sdcard/Resources/");
	#endif
	RM->Load(FM->Open((char *)"Manager/ResourceManager.txt"));
	FBShadow = new FrameBuffer;
	FBSelection = new FrameBuffer;
	FBRender = new FrameBuffer;
	FBSelection->Init();
	FBRender->Init();
	FBShadow->Init();
	RM->AddTexture("ShadowMap", &FBShadow->texture);

	SM->Init(FM->Open((char *)"Manager/SceneManager.txt"));
	SM->camera->position = Vector3(0.0, 0.0, 1.0);
	Screen = new Object;
	//textureShadow = FBShadow->texture;
	temp[0] = &FBShadow->texture;
	Screen->Init((char *)"Screen", Vector3(0.9, 0.9, 0), Vector3(0, 0, 0), 0.1, Vector4(0, 0, 0, 0), 1, temp, RM->GetModel((char *)"Quad"), RM->GetShader((char *)"Shader3"), SM->camera);
	//Screen2->Init("Screen2", Vector3(-0.5, -0.5, 0), Vector3(0, 0, 0), 0.5, &FB->texture , RM->GetModel("Quad"), RM->GetShader("Shader3"), SM->camera);

}

void Update()
{
	//cam.Go(0, 0, 0.01);
	//transform.SetRotationX();
	//transform = cam.LookAt(Vector3(0, 0, 0));
	
	//LOGI("\nFrame time: %f (s)", Timer::FrameTime() );
	SM->GetObject((char *)"Object3")->m_Position = SM->camera->position;
	SM->Update();
	SM->Draw(FBSelection->GetID(), true);
	if (IM->IsPointerDown() || IM->IsPointerDrag())
	{
		GLubyte pixels[4];
		glBindFramebuffer(GL_FRAMEBUFFER, FBSelection->fboId);
		glReadPixels(IM->m_PointerX , SCREEN_H - IM->m_PointerY - 1,  1,  1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
		//LOGI(" %d %d \n" ,IM->m_PointerX, IM->m_PointerY );
		//LOGI("%d %d %d %d \n", pixels[0], pixels[1], pixels[2], pixels[3]);
		Object* obj = SM->GetObject(Vector4(pixels[0], pixels[1], pixels[2], pixels[3]));
		if (obj != NULL)
			obj->m_Rotation.y += 1 * Timer::FrameTime();
		glBindFramebuffer(GL_FRAMEBUFFER, 0);
	}

	static int bfPointerX;
	static int bfPointerY;

	if (IM->IsPointerDown())
	{
		bfPointerX = IM->m_PointerX;
		bfPointerY = IM->m_PointerY;
	}

	if (IM->IsPointerDrag())
	{
		float dx = (IM->m_PointerX - bfPointerX) / 1000.0;
		float dy = (IM->m_PointerY - bfPointerY) / 1000.0;

        if(dx) {
            SM->camera->RotateRight(dx);
        }

        if(dy) {
            SM->camera->RotateDown(dy);
        }
		bfPointerX = IM->m_PointerX;
		bfPointerY = IM->m_PointerY;
	}
	rad = rad + 1 * Timer::FrameTime();
	SM->light->SetPosition(Vector3(cos(rad), 0.5, sin(rad)));
}

void Draw()
{
	SM->Draw();

	SM->Draw(FBShadow->GetID(), false, RM->GetShader("LightCast"));

	Matrix trans;
	trans.SetTranslation(Screen->m_Position);
	Matrix scale;
	scale.SetScale(Screen->m_Scale);
	Screen->Draw(scale * trans);
}

void Destroy()
{
	SAFE_DEL(SM);
	SAFE_DEL(RM);
	SAFE_DEL(FM);
	SAFE_DEL(IM);
	SAFE_DEL(FBRender);
	SAFE_DEL(FBSelection);
	SAFE_DEL(Screen);
}

void Key(int keyCode, bool isKeyDown)
{
	IM->OnKey(keyCode, isKeyDown);
	LOGI("\nKey code: %d - is key down: %d", keyCode, isKeyDown);
	//87 65 83 68
	if (isKeyDown && keyCode == 87)
	{
		SM->camera->RotateUp(1.0 * Timer::FrameTime());
	}
	if (isKeyDown && keyCode == 83)
	{
		SM->camera->RotateDown(1.0 * Timer::FrameTime());
	}
	if (isKeyDown && keyCode == 65)
	{
		SM->camera->RotateLeft(1.0 * Timer::FrameTime());
	}
	if (isKeyDown && keyCode == 68)
	{
		SM->camera->RotateRight(1.0 * Timer::FrameTime());
	}
	if (isKeyDown && keyCode == 39)
	{
		SM->camera->Go(0, 0, 1.0 * Timer::FrameTime());
	}
	if (isKeyDown && keyCode == 37)
	{
		SM->camera->Go(0, 0, -1.0 * Timer::FrameTime());
	}
	if (isKeyDown && keyCode == 38)
	{
		SM->camera->Go(0, -1.0 * Timer::FrameTime(), 0);
	}
	if (isKeyDown && keyCode == 40)
	{
		SM->camera->Go(0, 1.0 * Timer::FrameTime(), 0);
	}
	if (isKeyDown && keyCode == 90)
	{
		SM->camera->Go(-1.0 * Timer::FrameTime(), 0, 0);
	}
	if (isKeyDown && keyCode == 88)
	{
		SM->camera->Go(1.0 * Timer::FrameTime(), 0, 0);
	}

}

void Mouse(int type, int x, int y)
{

	//LOGI("\nMouse type: %d - (x = %d, y = %d)", type, x, y);
	IM->OnMouse(type, x, y);
}

#ifdef WIN32
int main()
{	
	InitWindow(SCREEN_W, SCREEN_H);
	
	RegisterInitFunc(Init);
	RegisterDrawFunc(Draw);
	RegisterUpdateFunc(Update);	
	RegisterDestroyFunc(Destroy);
	InputManager temp;
	RegisterKeyFunc(Key);
	RegisterMouseFunc(Mouse);

	WinLoop();
	//for (int i = -CIR_SIZE; i <= CIR_SIZE; i++)
	//{
	//	verticesData[i+CIR_SIZE+1].pos = Vector3( R * cos((i+0.0) / CIR_SIZE * PI), R * sin((i + 0.0) / CIR_SIZE * PI), 0.0f);
	//	std::cout<<verticesData[i+CIR_SIZE+1].pos.x<<' '<<verticesData[i+CIR_SIZE+1].pos.y<<std::endl;
	//}	
	//getch();
	return 0;
}

#endif

#ifdef _ANDROID

static void printGLString(const char *name, GLenum s) {
    const char *v = (const char *) glGetString(s);
    LOGI("GL %s = %s\n", name, v);
}

static void checkGlError(const char* op) {
    for (GLint error = glGetError(); error; error
            = glGetError()) {
        LOGI("after %s() glError (0x%x)\n", op, error);
    }
}

bool setupGraphics(int w, int h) {
    printGLString("Version", GL_VERSION);
    printGLString("Vendor", GL_VENDOR);
    printGLString("Renderer", GL_RENDERER);
    printGLString("Extensions", GL_EXTENSIONS);
	SCREEN_W = w;
	SCREEN_H = h;
    LOGI("setupGraphics(%d, %d)", w, h);

    glViewport(0, 0, w, h);
    checkGlError("glViewport");
    return true;
}

extern "C" {
    JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height);
    JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_step(JNIEnv * env, jobject obj);
    JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_Mouse(JNIEnv * env, jobject obj, jint type, jint x, jint y);
};

JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_Mouse(JNIEnv * env, jobject obj, jint type, jint x, jint y)
{
	LOGI("%d %d %d",type,x,y);
	Mouse(type, x, y);
};

JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height)
{
    setupGraphics(width, height);
	Init();
}

JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_step(JNIEnv * env, jobject obj)
{
	Timer::Tick();
	Draw();
	Update();
}

#endif